vt-d: enable PCI ACS P2P upstream forwarding
authorKeir Fraser <keir.fraser@citrix.com>
Mon, 23 Nov 2009 06:56:01 +0000 (06:56 +0000)
committerKeir Fraser <keir.fraser@citrix.com>
Mon, 23 Nov 2009 06:56:01 +0000 (06:56 +0000)
This patch enables P2P upstream forwarding in ACS capable PCIe
switches.  The enabling is conditioned on iommu_enabled variable.
This code solves two potential problems in virtualization environment
where a PCIe device is as signed to a guest domain using a HW iommu
such as VT-d:

1) Unintentional failure caused by guest physical address programmed
into the device's DMA that happens to match the memory address range
of other downstream ports in the same PCIe switch.  This causes the
PCI transaction to go to the matching downstream port instead of go to
the root complex to get translated by VT-d as it should be.

2) Malicious guest software intentionally attacks another downstream
PCIe device by programming the DMA address into the assigned device
that matches memory address range of the downstream PCIe port.

Corresponding ACS filtering code is already in upstream control panel
code that do not allow PCI device passthrough to guests if it is
behind a PCIe switch that does not have ACS capability or with ACS
capability but is not enabled.

Signed-off-by: Allen Kay <allen.m.kay@intel.com>
xen/arch/ia64/xen/pci.c
xen/drivers/passthrough/pci.c
xen/drivers/passthrough/vtd/iommu.c
xen/include/xen/pci.h
xen/include/xen/pci_regs.h

index c13e2e0a4f9e1e041bfb5b2eb574aa3b52491d8c..e8ad421515ecfc1d507c7dc8276656ccd153f8d9 100644 (file)
@@ -132,3 +132,8 @@ void pci_conf_write32(
     BUG_ON((bus > 255) || (dev > 31) || (func > 7) || (reg > 255));
     pci_sal_write(0, bus, (dev<<3)|func, reg, 4, data);
 }
+
+int pci_find_ext_capability(int seg, int bus, int devfn, int cap)
+{
+    return 0;
+}
index 843324598bfeae4798991396eebcf8ddc6e97bfb..d0bae649956996b52a2e92db8a1b44b8c580acbd 100644 (file)
@@ -100,6 +100,45 @@ struct pci_dev *pci_get_pdev_by_domain(struct domain *d, int bus, int devfn)
     return NULL;
 }
 
+/**
+ * pci_enable_acs - enable ACS if hardware support it
+ * @dev: the PCI device
+ */
+void pci_enable_acs(struct pci_dev *pdev)
+{
+    int pos;
+    u16 cap;
+    u16 ctrl;
+
+    u8 bus = pdev->bus;
+    u8 dev = PCI_SLOT(pdev->devfn);
+    u8 func = PCI_FUNC(pdev->devfn);
+
+    if ( !iommu_enabled )
+        return;
+
+    pos = pci_find_ext_capability(0, bus, pdev->devfn, PCI_EXT_CAP_ID_ACS);
+    if (!pos)
+        return;
+
+    cap = pci_conf_read16(bus, dev, func, pos + PCI_ACS_CAP);
+    ctrl = pci_conf_read16(bus, dev, func, pos + PCI_ACS_CTRL);
+
+    /* Source Validation */
+    ctrl |= (cap & PCI_ACS_SV);
+
+    /* P2P Request Redirect */
+    ctrl |= (cap & PCI_ACS_RR);
+
+    /* P2P Completion Redirect */
+    ctrl |= (cap & PCI_ACS_CR);
+
+    /* Upstream Forwarding */
+    ctrl |= (cap & PCI_ACS_UF);
+
+    pci_conf_write16(bus, dev, func, pos + PCI_ACS_CTRL, ctrl);
+}
+
 int pci_add_device(u8 bus, u8 devfn)
 {
     struct pci_dev *pdev;
@@ -119,6 +158,7 @@ int pci_add_device(u8 bus, u8 devfn)
             goto out;
 
         list_add(&pdev->domain_list, &dom0->arch.pdev_list);
+        pci_enable_acs(pdev);
     }
 
 out:
index 28eca9f6d1537230575768629ffd7a4cdde9ecb3..3efcc89e21b6b46500c21d5defc4fa634e9d9354 100644 (file)
@@ -1628,6 +1628,7 @@ static void setup_dom0_devices(struct domain *d)
             pdev->domain = d;
             list_add(&pdev->domain_list, &d->arch.pdev_list);
             domain_context_mapping(d, pdev->bus, pdev->devfn);
+            pci_enable_acs(pdev);
             if ( ats_device(0, pdev->bus, pdev->devfn) )
                 enable_ats_device(0, pdev->bus, pdev->devfn);
         }
index abb8584ed3912bc75f5eed4b8fa1f2978e3ca0c4..1740698cc6471269dec1add757ca810992e32a18 100644 (file)
@@ -116,5 +116,6 @@ int pci_find_ext_capability(int seg, int bus, int devfn, int cap);
 
 int msixtbl_pt_register(struct domain *d, int pirq, uint64_t gtable);
 void msixtbl_pt_unregister(struct domain *d, int pirq);
+void pci_enable_acs(struct pci_dev *pdev);
 
 #endif /* __XEN_PCI_H__ */
index 0615bd8cc9195cb0c4f0dc4b03cc687a82076d67..b92a1710520fde6fd367a5144d39dc4b005de305 100644 (file)
 #define PCI_EXT_CAP_ID_VC      2
 #define PCI_EXT_CAP_ID_DSN     3
 #define PCI_EXT_CAP_ID_PWR     4
-#define PCI_EXT_CAP_ID_ARI     0xE
-#define PCI_EXT_CAP_ID_ATS     0xF
-#define PCI_EXT_CAP_ID_IOV     0x10
+#define PCI_EXT_CAP_ID_ACS     13
+#define PCI_EXT_CAP_ID_ARI     14
+#define PCI_EXT_CAP_ID_ATS     15
+#define PCI_EXT_CAP_ID_IOV     16
 
 /* Advanced Error Reporting */
 #define PCI_ERR_UNCOR_STATUS   4       /* Uncorrectable Error Status */
 #define HT_CAPTYPE_GEN3                0xD0    /* Generation 3 hypertransport configuration */
 #define HT_CAPTYPE_PM          0xE0    /* Hypertransport powermanagement configuration */
 
+/* Access Control Service */
+#define PCI_ACS_CAP            0x04    /* ACS Capability Register */
+#define  PCI_ACS_SV            0x01    /* Source Validation */
+#define  PCI_ACS_TB            0x02    /* Translation Blocking */
+#define  PCI_ACS_RR            0x04    /* P2P Request Redirect */
+#define  PCI_ACS_CR            0x08    /* P2P Completion Redirect */
+#define  PCI_ACS_UF            0x10    /* Upstream Forwarding */
+#define  PCI_ACS_EC            0x20    /* P2P Egress Control */
+#define  PCI_ACS_DT            0x40    /* Direct Translated P2P */
+#define PCI_ACS_CTRL           0x06    /* ACS Control Register */
+#define PCI_ACS_EGRESS_CTL_V   0x08    /* ACS Egress Control Vector */
 
 #endif /* LINUX_PCI_REGS_H */